﻿//-----------------------------------------------------------------------
// <copyright file="AppUpdateInstallerViewModel.cs" author="Artak Mkrtchyan">
//     Creative Commons - GNU General Public License (http://creativecommons.org/licenses/GPL/2.0/
// </copyright>
// <author>Artak Mkrtchyan</author>
// <email>mkArtak@gmail.com</email>
// <date>20/03/2013</date>

using AppUpdateInstaller.Model;
using ConsoleHoster.Common.Utilities;
using ConsoleHoster.Common.Utilities.IO;
using ConsoleHoster.Common.Utilities.Threading;
using ConsoleHoster.Common.ViewModel;
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Input;

namespace AppUpdateInstaller.ViewModel
{
	public class AppUpdateInstallerViewModel : ViewModelBase, INotifyPropertyChanged
	{
		private readonly Lazy<ICommand> finishInstallationCommand;
		private readonly InstallationProcess installationProcess;
		private readonly Action appShutdownLogic;

		private bool installationSucceeded = true;
		private double progress = 0.0;
		private string status = String.Empty;
		private bool installationCompleted = false;

		public AppUpdateInstallerViewModel(IDispatcher argDispatcher, string[] args)
			: this(argDispatcher, new InstallationStageFactory(args, new ProcessManager(), new FileSystemContentProvider(), () => App.Current.Shutdown()), () => App.Current.Shutdown())
		{
		}

		public AppUpdateInstallerViewModel(IDispatcher argDispatcher, IInstallationStageFactory argStageFactory, Action argShutdownLogic)
			: base(argDispatcher, null)
		{
			this.appShutdownLogic = argShutdownLogic;

			this.installationProcess = new InstallationProcess(null);
			this.installationProcess.AddInstallationStage(argStageFactory.Create());
			this.installationProcess.StageStarting += OnInstallationProcess_StageStarting;
			this.installationProcess.StageFinished += OnInstallationProcess_StageFinished;
			this.installationProcess.InstallationFailure += OnInstallationProcess_InstallationFailure;

			this.finishInstallationCommand = new Lazy<ICommand>(() => new RelayCommand(o => this.FinishInstallation()));
		}

		private void OnInstallationProcess_StageStarting(InstallationProcess argSender, InstallationProgressEventArgs argEA)
		{
			this.Progress = Math.Min(100, argEA.PercentComplete + 1);
		}

		private void OnInstallationProcess_StageFinished(InstallationProcess argSender, InstallationProgressEventArgs argEA)
		{
			this.Progress = Math.Max(this.Progress, argEA.PercentComplete);
		}

		private void OnInstallationProcess_InstallationFailure(InstallationProcess argSender, InstallationProgressEventArgs argEA)
		{
			this.InstallationCompleted = true;
			this.InstallationSucceeded = false;
		}

		public async Task StartUpdateAsync()
		{
			this.Status = "Waiting for the running processes to close...";
			await this.WaitForPendingProcessesToFinish();

			await this.installationProcess.RunAsync();

			this.Status = "Completed !";
			this.InstallationCompleted = true;
		}

		private Task WaitForPendingProcessesToFinish()
		{
			return Task.Factory.StartNew(() =>
				{
					bool tmpIsRunning = true;
					do
					{
						Thread.Sleep(500);
						Process[] tmpProcesses = Process.GetProcessesByName("ConsoleHoster.exe");
						tmpIsRunning = tmpProcesses.Any();
						foreach (Process tmpProcess in tmpProcesses)
						{
							tmpProcess.Dispose();
						}
					} while (tmpIsRunning);
				});
		}

		private void FinishInstallation()
		{
			if (this.appShutdownLogic != null)
			{
				this.appShutdownLogic();
			}
		}

		public bool InstallationCompleted
		{
			get
			{
				return this.installationCompleted;
			}
			set
			{
				this.SetPropertyValue<bool>("InstallationCompleted", ref this.installationCompleted, value);
			}
		}

		public bool InstallationSucceeded
		{
			get
			{
				return this.installationSucceeded;
			}
			private set
			{
				if (this.SetPropertyValue<bool>("InstallationSucceeded", ref this.installationSucceeded, value))
				{
					this.NotifyPropertyChange("InstallationFailed");
				}
			}
		}

		public bool InstallationFailed
		{
			get
			{
				return !this.InstallationSucceeded;
			}
		}

		public ICommand FinishInstallationCommand
		{
			get
			{
				return this.finishInstallationCommand.Value;
			}
		}

		public double Progress
		{
			get
			{
				return this.progress;
			}
			private set
			{
				this.SetPropertyValue<double>("Progress", ref this.progress, value);
			}
		}

		public string Status
		{
			get
			{
				return this.status;
			}
			set
			{
				this.SetPropertyValue<string>("Status", ref this.status, value);
			}
		}
	}
}